package com.baosight.xCyber.util;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.baosight.xCyber.model.TagNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;


/**
 * 建立字典树的算法，并遍历字典树找出父节点名称
 *
 * @author 吴起航
 */
public class InitTree {

    private static Map<String, TagNode> codeMapTemp;
    private static Logger log = LoggerFactory.getLogger(InitTree.class.getSimpleName());
    //用于储存所有的叶子节点上的数据
    public Map<String,TagNode> treeTemp;
    //用于储存树结构的数据
    public Map<String,TagNode> mainTemp;

    private String rootName="root";

    private Map<String, TagNode> tagNodeMap = new HashMap<>();
    /**
     * 初始化根节点
     */
    private TrieNode root = new TrieNode();

    private class TrieNode {
        TrieNode preNode = null;    //获取父节点
        boolean isEnd = true;  //尾字符标识
        int deep = 0;       //做hash使用，当出现相同字符时，用于定位指定字符
        char content = 0;   //当前节点的字符
        boolean childFlag = true; //标识树的末端
        LinkedList<TrieNode> child = new LinkedList<>();//获取子节点

        TrieNode() {
        }

        TrieNode(char content) {
            this.content = content;
            this.isEnd = false;
        }

        public LinkedList<TrieNode> getChild() {
            return child;
        }

        public void setChild(LinkedList<TrieNode> child) {
            this.child = child;
        }

        void setPreNode(TrieNode node) {
            preNode = node;
        }

        TrieNode getPreNode() {
            return preNode;
        }


        /**
         * child中是否有此Node
         *
         * @param character 保存的char
         * @return 存在返回不存在返回Null
         */
        TrieNode getNode(Character character) {
            for (TrieNode aChild : child) {
                if (aChild.content == character) {
                    return aChild;
                }
            }
            return null;
        }
    }

    /**
     * @param codeMap 字典集合，用于解析成树结构
     * @param titleMap 根数据集合，用于初始化根节点（根节点默认为空字符）
     * @param dataMap 节点数据集合，用于添加至树结构的各节点关节处
     * 用字典表map建立字典树
     */
    public String initMap(Map<String, TagNode> codeMap,
                                               Map<String,TagNode> titleMap,
                                               Map<String,TagNode> dataMap) {
        treeTemp = new HashMap<>();
        mainTemp = new HashMap<>();
        codeMapTemp = codeMap;
        TagNode rootNode =codeMapTemp.get("root");
        //建立字典解析树
        //判断树是否已经初始化
        if(root.childFlag){
            for (Map.Entry<String, TagNode> entry : codeMap.entrySet()) {
                TagNode tagNode = entry.getValue();
                buildTrieTree(tagNode);
            }
        }
        log.info(rootNode.treeName+":字典树建立完毕");

        //遍历在树上添加数据节点
        for (Map.Entry<String, TagNode> entry : dataMap.entrySet()) {
            TagNode tagNode = entry.getValue();
            addTagNode(tagNode);
        }
        log.info(rootNode.treeName+":获得目标树结构");
        Map<String, TagNode> returnMap = returnMap();

        //初始化根节点
        if (titleMap!=null&&titleMap.size() != 0) {
            returnMap = GetRoot.initRoot(titleMap, returnMap, mainTemp);
        }else{
            mainTemp.put("root",returnMap.get("root"));
        }
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
        String[] filterList=new String[]{
                "showName",
                "itemFlag",
                "remarkName",
                "source",
                "serviceId",
                "unit",
                "parentId",
                "treeName",
                "desc",
                "extraParam",
                "lazyLoad"
        };
        Arrays.stream(filterList).forEach(filter.getExcludes()::add);

        return JSONObject.toJSONString(returnMap.get("root"), filter);
    }


    /**
     * 建立字典树的算法，并遍历字典树找出父节点名称
     *
     * @param tagNode 需要解析的节点
     */
    private void buildTrieTree(TagNode tagNode) {
        int deep = 0;
        String tagName = tagNode.tagName;

        //初始化根节点
        TrieNode currNode = root;
        while (deep < tagName.length()) {
            /* 判断当前node的child，如果为空直接添加，不为空，
             * 查找是否含有，不含有则新建并设为currNode，含有则找到并设置为currNode
             */
            char c = tagName.charAt(deep);
            TrieNode node = currNode.getNode(c);

            //判断当前字符是否在其子节点字符中
            if (node != null) {
                //若是，则设置下次循环的主节点为当前字符
                currNode = node;
            } else {
                //对于字典表数据，若不是，则新建节点：设置其父节点为根节点,并添加至根节点的子节点
                //深度+1
                //设置下次循环的主节点为当前字符
                node = new TrieNode(c);
                node.setPreNode(currNode);
                node.deep = deep + 1;
                node.childFlag = true;
                currNode.childFlag = false;
                currNode.child.add(node);
                currNode = node;
            }
            //判断当前字符是否已经遍历完毕
            if (deep == tagName.length() - 1) {
                currNode.isEnd = true;
            }
            deep++;
        }
    }

    private void addTagNode(TagNode tagNode) {
        TrieNode currNode = root;
        int deep = 0;
        String tagName = tagNode.tagName;
        String parentNodeName;
        TagNode parentNode;

        if(tagNode.parentId!=""
                && !tagNode.parentId.contains("other")
                && codeMapTemp.containsKey(tagNode.parentId)){
            parentNodeName=tagNode.parentId;
            tagName = parentNodeName;
        }
        while (deep < tagName.length()) {
            char c = tagName.charAt(deep);
            TrieNode node = currNode.getNode(c);
            //判断当前字符是否在其子节点字符中
            if (node != null) {
                //若是，则设置下次循环的主节点为当前字符
                currNode = node;

                if(node.isEnd && deep == tagName.length()-1 && !node.childFlag){
                    //节点名称的字符遍历完毕，处于树上某节点处且非树的末梢
                    parentNode = codeMapTemp.get(tagName);
                    addOtherTag(tagNode,parentNode);
                    deep--;
                    if(parentNode.tagName.equals(tagName)){
                        deep++;
                    }
                    traverseTree(parentNode,node.preNode,deep);
                    break;
                }
                if (node.childFlag) {
                    //节点名称的字符遍历完毕，遍历到树的末梢
                    tagName = tagName+" ";
                    parentNodeName = tagName.substring(0, deep + 1);

                    parentNode = codeMapTemp.get(parentNodeName);
                    tagNode.parentId = parentNodeName;
                    tagNode.treeName = parentNode.treeName;
                    tagNode.setItems(null);
                    List<TagNode> temp = parentNode.getItems();
                    temp.add(tagNode);
                    parentNode.setItems(temp);
                    parentNode.setItemFlag(false);
                    treeTemp.put(tagNode.tagName,tagNode);

                    //直接存放对象
                    tagNodeMap.put(parentNodeName, parentNode);
                    traverseTree(parentNode,node.preNode,deep);
                    break;
                }
                deep++;
            } else {
                node = currNode;
                //在当前node的子节点下未找的该节点，保留符合父节点不符合根节点的数据
                if (node.content != 0) {
                    //遍历到上一个存在标识的节点
                    while (!node.isEnd) {
                        node = node.preNode;
                        deep--;
                    }
                    parentNodeName = tagName.substring(0, deep);
                    if(parentNodeName.equals("")){
                        parentNodeName="root";
                    }
                    parentNode = codeMapTemp.get(parentNodeName);

                    //判断otherTag有没有初始化
                    addOtherTag(tagNode,parentNode);
                    deep--;
                    traverseTree(parentNode,node.preNode,deep);
                }
                break;
            }
        }
    }


    private Map<String, TagNode> returnMap() {
        //保存完整的树节点结构
        tagNodeMap.put("root", codeMapTemp.get("root"));
        return tagNodeMap;
        //分别保存树结构+叶子节点结构
    }

    /**
     *
     * @param tagNode 点末梢（结构点）
     * @param node 当前节点
     * @param deep 点的位置
     */
    private void traverseTree(TagNode tagNode,TrieNode node,int deep){
        String tagName = tagNode.tagName;
        String parentTagName;
        TagNode parentTag;

        while (tagNode.parentId == "" && tagNode.tagName != "") {
            if (node.isEnd) {
                parentTagName = tagName.substring(0, deep);
                if(parentTagName.equals("")){
                    parentTagName = "root";
                }
                parentTag =codeMapTemp.get(parentTagName);
                tagNode.parentId = parentTagName;
                parentTag.setItemFlag(true);
                List<TagNode> childs = parentTag.getItems();
                if (!childs.contains(tagNode)) {
                    childs.add(tagNode);
                    parentTag.setItems(childs);
                }
                mainTemp.put(tagNode.tagName,tagNode);
                tagNode = parentTag;
            }
            node = node.preNode;
            deep--;
        }
    }

    /**
     * 在父节点下添加"其他"结构的节点
     * @param parentNode 父节点
     * @param tagNode 当前节点
     */
    private void addOtherTag(TagNode tagNode,TagNode parentNode){
        if (parentNode.otherTag == null) {
            TagNode otherTag = new TagNode();
            parentNode.otherTag = otherTag;
            otherTag.tagName = parentNode.tagName+"_other";
            otherTag.treeName = parentNode.treeName;
            otherTag.showName = "其他";
            otherTag.parentId = parentNode.tagName;
            otherTag.setItemFlag(false);
            parentNode.getItems().add(otherTag);
            parentNode.setItemFlag(true);
            mainTemp.put(otherTag.tagName,otherTag);
        }
        TagNode otherTag = parentNode.otherTag;
        tagNode.parentId = otherTag.tagName;
        tagNode.treeName = otherTag.treeName;
        tagNode.setItems(null);
        otherTag.getItems().add(tagNode);
        tagNodeMap.put(otherTag.tagName,otherTag);
        treeTemp.put(tagNode.tagName,tagNode);
    }

    public String initCommonMap(Map<String, TagNode> codeMap,
                                 Map<String,TagNode> titleMap,
                                 Map<String,TagNode> dataMap){
        String tagName,parentName;
        TagNode relation;
        mainTemp = new HashMap<>();
        treeTemp = new HashMap<>();
        int i=1;
        for(Map.Entry<String,TagNode> entry:codeMap.entrySet()){
            tagName=entry.getKey();
            relation=entry.getValue();
            parentName=relation.getShowName();
            if(!dataMap.containsKey(tagName)){
                continue;
            }
            if(!mainTemp.containsKey(parentName)){
                TagNode parentNode=new TagNode();
                parentNode.tagName=parentName;
                parentNode.orderNo=i;
                parentNode.treeName=relation.treeName;
                parentNode.showName=parentName;
                parentNode.parentId="root";
                TagNode rootNode=codeMap.get("root");
                rootNode.getItems().add(parentNode);
                mainTemp.put(parentName,parentNode);
                i++;
            }
            TagNode tagNode = dataMap.get(tagName);
            TagNode parentNode=mainTemp.get(parentName);
            tagNode.parentId=parentName;
            tagNode.setItems(null);
            tagNode.treeName=parentNode.treeName;
            parentNode.getItems().add(tagNode);
            treeTemp.put(tagName,tagNode);
        }
        //初始化根节点
        mainTemp.put("root",codeMap.get("root"));
        if (titleMap!=null&&titleMap.size() != 0) {
            rootName="title1";
            mainTemp = GetRoot.initRoot(titleMap, null, mainTemp);
        }

        SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
        String[] filterList=new String[]{
                "showName",
                "itemFlag",
                "remarkName",
                "source",
                "serviceId",
                "unit",
                "parentId",
                "treeName",
                "desc",
                "extraParam",
                "lazyLoad",
                "attr",
                "orderNo"
        };
        Arrays.stream(filterList).forEach(filter.getExcludes()::add);

        String result = JSONObject.toJSONString(mainTemp.get(rootName), filter);
        for(Map.Entry<String, TagNode> title:titleMap.entrySet()){
            result=result.replaceAll(title.getKey(),title.getValue().showName);
        }
        return result;
    }

}
